classdef ziPlotter < EquipmentControl.ZI.ziModule.ziModule
    %ZIPLOTTER implements polling of data from zi devices
    %   example: ziPlotter('dev3546', session)
    
    properties
        data_polled
        window_time        
    end
    
    methods
        function obj = ziPlotter(dev)
            %ZIPLOTTER Construct an instance of ziPlotter module
            %   example: ziPlotter('dev3546', session)
            obj@EquipmentControl.ZI.ziModule.ziModule('plotter', dev);
            
            obj.subscribed_paths = [];
        end
        
        %% Run
        function res = run(obj, varargin)
            % run the module
            clear textprogressbar;
            
            p=inputParser;
            isnonneg = @(x) x > 0;
            func_hdl_test = @(x) isa(x, 'function_handle') || strcmp(x, 'none');
            p.addParameter('single_poll_duration', 0.1, isnonneg);
            p.addParameter('total_duration', 600.0, isnonneg);
            p.addParameter('window_time', 10.0, isnonneg);
            p.addParameter('timeout', 0.5, isnonneg);
            p.addParameter('processMonitor', @plotData, func_hdl_test);
            p.parse(varargin{:});
            
            if ~isvalid(obj.monitor_hdl)
                obj.createMonitor;
            end
            
            processMonitor = p.Results.processMonitor;
            obj.window_time = p.Results.window_time;
            obj.monitor_plot_init();
            
            obj.syncModule();
            obj.data_polled = [];
            textprogressbar( sprintf('%s Polling Data,\t', datestr(now,'yyyy-mm-dd HH:MM:SS,FFF')) );   tic; 
            while toc < p.Results.total_duration
                pause(p.Results.single_poll_duration/10.0);
                textprogressbar( toc/p.Results.total_duration *100);
                
                tmp = obj.poll_data(p.Results.single_poll_duration, 'timeout', p.Results.timeout);
                tmp.clockbase = obj.clockbase;
                obj.data_polled = [obj.data_polled, tmp];
               
                if ~strcmp(processMonitor, 'none')
                    plt_arr = EquipmentControl.ZI.ziData.ziPlotterDataArray(obj.data_polled);
                    data = arrayfun(@(curve) plt_arr.getNodeData(curve.channel, curve.node), obj.curve_info);
                    processMonitor(obj, obj.monitor_hdl, data);
                end
            end
            textprogressbar( 100 ); textprogressbar('.\t done.');
            res = EquipmentControl.ZI.ziData.ziPlotterDataArray(obj.data_polled);
            obj.measured_data = res;
        end
        
        function data = poll_data(obj, duration, varargin)
            % poll data
            p=inputParser;
            isnonneg = @(x) x > 0;
            p.addRequired('duration', isnonneg);
            p.addParameter('timeout', 1, isnonneg);
            p.parse(duration, varargin{:});
            
            data = obj.pollModule(p.Results.duration, p.Results.timeout);
        end
        
        %% Subscribe/Unsubscribe 
        function obj = subscribeChannel(obj, channel, nodes)
            % subscribe channels
            % input: channel - available demod channels, nodes - {'x', 'y',
            % 'r', 'frequency','phase', 'theta'}
            L=log4m.getLogger;
            p = inputParser;
            chTest = @(x) ismember(channel, obj.available_demod_channel);
            nodeTest = @(x) all(ismember(nodes, {'x', 'y', 'r', 'frequency','phase', 'theta', 'auxin0', 'auxin1'}));
            p.addRequired('channel', chTest);
            p.addRequired('nodes', nodeTest);
            p.parse(channel, nodes);
            
            obj.zi.enableDataTransfer(channel);
            paths = obj.make_path(channel, nodes);

            channelPath = paths{1}.path_string;
            obj.zi.command('subscribe', channelPath);
            L.info('ziPlotter::subscribeChannel', sprintf('Node %s subscribed.', channelPath));
        end
        
        function obj = unsubscribeChannel(obj)
            % unsubscribe channels
            L=log4m.getLogger;
            if ~isempty(obj.subscribed_paths)
                subs_ch = unique( cellfun(@(p) p.path_string ,obj.subscribed_paths, 'UniformOutput', false) );
                for k = 1:length(subs_ch)
                    obj.zi.command('unsubscribe', subs_ch{k})
                    L.info('ziPlotter::unsubscribeChannel', sprintf('Node %s unsubscribed.', subs_ch{k}));
                end
                obj.syncModule();
            end
        end
        
        function obj = reset(obj)
            % reset the module: call ziModule reset and clear obj.data_polled
            reset@EquipmentControl.ZI.ziModule.ziModule(obj);
            obj.unsubscribeChannel();
            obj.data_polled = [];
        end
        
        %% monitor 
        function plotData(obj, ~, data)
            % default monitor function
            for k=1:length(obj.curve_info)
                obj.curves(k).XData = data(k).time - max(data(k).time);
                obj.curves(k).YData = data(k).value;
            end
            drawnow limitrate
        end
        
        function monitor_plot_init(obj)
            % monitor plot initialization
            xlim(obj.monitor_hdl, [-obj.window_time 0]);
            color_array = obj.monitor_hdl.ColorOrder;
            len_color = size(color_array, 1);
            obj.curves = arrayfun(@(k) line(obj.monitor_hdl, ...
                                           'Color', color_array(mod(k, len_color)+1,:), ...
                                           'DisplayName', obj.curve_info(k).str), ...
                                 1:length(obj.curve_info));
            obj.monitor_hdl.YScale = 'linear';
            obj.monitor_hdl.XScale = 'linear';
            legend('show');
            grid on;
        end
    end

    %% private
    methods (Access = private)
        function paths = make_path(obj, ch, nodes)
            if ~iscell(nodes)
                nodeCell = {nodes};
            else
                nodeCell = nodes;
            end
            
            paths = cellfun(@(node) EquipmentControl.ZI.ziData.ziPath(obj.device, 'demods', ch, 'sample', node), nodeCell, 'UniformOutput', false);
            obj.subscribed_paths = [obj.subscribed_paths paths];
            obj.curve_info=cellfun(@(p) p.channel_node_str, obj.subscribed_paths);
        end
    end
    

end

